#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <iostream>
#include <vector>
#include <unistd.h>
#include <sys/stat.h>
#include <malloc.h>
#include <sys/types.h>
#include <fcntl.h>
#include <dirent.h>
#include <grp.h>
#include <pwd.h>
#include <time.h>
#include <utime.h>
using namespace std;
struct fnode
{
    struct fnode *next; //下一个成员
    char name[50];      //当前成员文件名
};

void string_trim(string &str, vector<string> &cmd_arg) //命令裁减，参数分离，大小写转换
{
    int s = str.find_first_not_of(" ");
    int e = str.find_last_not_of(" ");
    str = str.substr(s, e - s + 1);
    for (int i = 0, j = 0; j < str.size(); j++)
    {
        if (str[j] == ' ')
        {
            cmd_arg.push_back(str.substr(i, j - i));
            i = j + 1;
        }
        if (j == str.size() - 1)
            cmd_arg.push_back(str.substr(i, j - i + 1));
    }
    //标识符大小写统一
    for (int i = 0; i < cmd_arg[0].size(); i++)
        if (cmd_arg[0][i] < 'a')
            cmd_arg[0][i] = cmd_arg[0][i] - 'A' + 97;
    return;
}
void Fun_History(vector<string> History) //打印用户历史命令
{
    for (int i = 0; i < History.size(); i++)
        cout << History[i] << endl;
    return;
}
void Fun_Cd(string new_path) //进入新的文件夹
{
    int errnum;
    errnum = chdir(new_path.c_str());
    if (errnum == 0)
        //cout << "成功进入新文件夹--->" << new_path << endl;
        ;
    else
        cout << "切换失败，请检查路径是否正确" << endl;
    return;
}
void Fun_Rm(string folder_name)
{
    DIR *dp = NULL;
    DIR *dpin = NULL;
    char *pathname = (char *)malloc(100);
    struct dirent *dirp;
    if (folder_name.find('.') != folder_name.npos)
    {
        remove(folder_name.c_str());
        return;
    }
    dp = opendir(folder_name.c_str());
    if (dp == NULL)
    {
        cout << "目标文件夹不存在" << endl;
        return;
    }

    while ((dirp = readdir(dp)) != NULL)
    {
        if (strcmp(dirp->d_name, "..") == 0 || strcmp(dirp->d_name, ".") == 0)
            continue;
        strcpy(pathname, folder_name.c_str());
        strcat(pathname, "\\");
        strcat(pathname, dirp->d_name);
        dpin = opendir(pathname);
        if (dpin != NULL)
            Fun_Rm(pathname);
        else
            remove(pathname);
        strcpy(pathname, "");
        closedir(dpin);
        dpin = NULL;
    }
    rmdir(folder_name.c_str());
    closedir(dp);
    free(pathname);
    pathname = NULL;
    dirp = NULL;
}
void Fun_Cp(string Folder_Src, string Folder_Des);
void Fun_Ls(string Folder_name);

int main()
{
    //命令标识副
    string Command[]{
        "cd",
        "ls",
        "pwd",
        "rm",
        "mkdir",
        "mv",
        "cp",
        "exit",
        "history"};
    //终端打印行
    string myshell_left = "My_Shell[";
    string myshell_right = "]>";
    char path_buf[50];
    char path_buf_temp[50];
    //用户输入命令
    string my_commandline;
    int my_command;
    char *temp_cmdline;
    //用户命令参数
    vector<string> cmd_arg;
    //状态标识符
    int state = 1;
    //历史命令记录
    vector<string> History;
    //打印进入信息
    cout << "进入终端命令模式，请输入指令" << endl;
    while (state)
    {
        //获取当前工作路径
        getcwd(path_buf, 100);
        cout << myshell_left << path_buf << myshell_right;
        //获取用户输入命令行
        getline(cin, my_commandline, '\n');
        //将此次命令行记录
        History.push_back(my_commandline);
        //参数分离
        cmd_arg.clear();
        string_trim(my_commandline, cmd_arg);
        for (int i = 0; i < 10; i++)
        {
            if (cmd_arg[0] == Command[i])
            {
                my_command = i;
                break;
            }
            if (i == 9)
                my_command = 9;
        }

        switch (my_command)
        {
        case 0: //cd
            //cout << "here_cd" << endl;
            if (cmd_arg.size() != 2)
                cout << "cd命令参数错误，请检查" << endl;
            else
                Fun_Cd(cmd_arg[1]);
            break;
        case 1: //ls
            cout << "here_ls" << endl;
            if (cmd_arg.size() == 1)
                Fun_Ls(path_buf);
            else
                Fun_Ls(cmd_arg[1]);
            break;
        case 2: //pwd
            cout << "here_pwd" << endl;
            cout << "当前工作目录的路径为--->" << path_buf << endl;
            break;
        case 3: //rm
            cout << "here_rm" << endl;
            Fun_Rm(cmd_arg[1].c_str());
            break;
        case 4: //mkdir
            cout << "here_mkdir" << endl;
            if (cmd_arg.size() != 2)
                cout << "命令参数错误，请检查" << endl;
            else
            {
                mkdir(cmd_arg[1].c_str(), S_IRWXU);
                cout << "成功建立文件夹" << cmd_arg[1].c_str() << endl;
            }
            break;
        case 5: //mv
            cout << "here_mv" << endl;
            if (cmd_arg.size() != 3)
                cout << "命令参数错误，请检查" << endl;
            else
            {
                Fun_Cp(cmd_arg[1], cmd_arg[2]);
                Fun_Rm(cmd_arg[1]);
            }
            break;
        case 6: //cp
            strcpy(path_buf, path_buf_temp);
            if (cmd_arg.size() != 3)
                cout << "命令参数错误，请检查" << endl;
            else
                Fun_Cp(cmd_arg[1], cmd_arg[2]);

            break;
        case 7: //exit
            state = 0;
            break;
        case 8: //history
            Fun_History(History);
            break;

        default: //error
            cout << "指令错误，请输入正确指令" << endl;
            break;
        }
    }
    cout << "退出My_Shell" << endl;
    return 0;
}

void Fun_Ls_output_perm(mode_t mode) //输出文件属性
{
    char type[7] = {'p', 'c', 'd', 'b', '-', 'l', 's'};
    int index = ((mode >> 12) & 0xF) / 2;
    printf("%c", type[index]);

    char *perm[8] = {"---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"};
    printf("%s", perm[mode >> 6 & 07]);
    printf("%s", perm[mode >> 3 & 07]);
    printf("%s", perm[mode >> 0 & 07]);
}

void Fun_Ls_output_group(uid_t uid, gid_t gid) //输出文件权限
{
    struct passwd *user;
    user = getpwuid(uid);

    printf(" %s", user->pw_name);

    struct group *group;

    group = getgrgid(gid);
    printf(" %s", group->gr_name);
}

void Fun_Ls_output_time(time_t mytime) //输出文件时间
{
    char buf[256];
    memset(buf, '\0', 256);
    ctime_r(&mytime, buf);
    buf[strlen(buf) - 1] = '\0';
    printf(" %s", buf);
}

void Fun_Ls_output_info(struct fnode *head) //输出文件信息
{
    struct fnode *temp = head;
    while (temp != NULL)
    {
        struct stat mystat;
        if (stat(temp->name, &mystat) == -1)
        {
            perror("stat");
            exit(EXIT_FAILURE);
        }
        Fun_Ls_output_perm(mystat.st_mode);
        printf(" %4d", mystat.st_mode);
        Fun_Ls_output_group(mystat.st_uid, mystat.st_gid);
        printf(" %8ld", mystat.st_size);
        Fun_Ls_output_time(mystat.st_mtime);
        printf(" %s\n", temp->name);
        temp = temp->next;
    }
}

struct fnode *insert_list(struct fnode *temp, struct fnode *linklist)
{
    if (linklist == NULL)
    {
        linklist = temp;
    }
    else
    {
        struct fnode *node;
        node = linklist;
        while (node->next != NULL)
        {
            node = node->next;
        }
        node->next = temp;
    }
    return linklist;
}

void Fun_Ls_free(struct fnode *linklist) //释放
{
    struct fnode *temp;
    temp = linklist;
    while (temp != NULL)
    {
        temp = linklist->next;
        free(linklist);
        linklist = temp;
    }
}

void Fun_Ls(string Folder_name)
{

    struct fnode *linklist = NULL;
    struct stat stat_info;

    if (stat(Folder_name.c_str(), &stat_info) == -1)
    {
        perror("stat");
        exit(EXIT_FAILURE);
    }

    if (S_ISREG(stat_info.st_mode)) //普通文件
    {
        printf("It is a normal file\n");
        struct fnode *temp = (struct fnode *)malloc(sizeof(struct fnode)); //为当前文件结构申请内存空间

        if (temp == NULL)
        {
            perror("malloc error");
            exit(EXIT_FAILURE);
        }
        temp->next = NULL;
        memset(temp->name, '\0', 50);
        memcpy(temp->name, Folder_name.c_str(), strlen(Folder_name.c_str()));
        linklist = insert_list(temp, linklist); //将该文件插入linklist中
        Fun_Ls_output_info(linklist);           //输出文件的所有信息
    }
    else if (S_ISDIR(stat_info.st_mode)) //是否是路径
    {
        printf("It is a dir \n");

        char buf[50];
        getcwd(buf, 128); //获取当前工作路径并将其拷贝到buf中
        DIR *dirp = NULL;
        dirp = opendir(Folder_name.c_str()); //打开当前路径
        if (dirp == NULL)
        {
            perror("open dir");
            exit(EXIT_FAILURE);
        }
        struct dirent *entp = NULL;
        while (entp = readdir(dirp))
        {
            struct fnode *temp = (struct fnode *)malloc(sizeof(struct fnode));
            if (temp == NULL)
            {
                perror("malloc");
                exit(EXIT_FAILURE);
            }
            temp->next = NULL;
            memset(temp->name, '\0', 50);
            memcpy(temp->name, entp->d_name, strlen(entp->d_name));
            linklist = insert_list(temp, linklist);
        }
        chdir(Folder_name.c_str()); //改变当前路径
        closedir(dirp);
        Fun_Ls_output_info(linklist);
        chdir(buf);
    }
    Fun_Ls_free(linklist);
    return;
}

void Fun_Cp_Change_P(char *src, char *dest) //修改当前文件夹路径
{
    strcat(src, (char *)"/");
    strcat(src, dest);
}

void Fun_Cp_Change_A(const char *src, const char *dst) //复制源文件属性并修改目标文件
{
    struct stat attr_of_src;
    lstat(src, &attr_of_src);
    chmod(dst, attr_of_src.st_mode);
    chown(dst, attr_of_src.st_uid, attr_of_src.st_gid);
    struct timeval time_buf[2];
    time_buf[1].tv_sec = attr_of_src.st_mtime;
    time_buf[0].tv_sec = attr_of_src.st_atime;
    struct utimbuf tbuf;
    tbuf.actime = attr_of_src.st_atime;
    tbuf.modtime = attr_of_src.st_mtime;
    utime(dst, &tbuf);
    struct stat dst_attr_of_src;
    lstat(dst, &dst_attr_of_src);
}

void Fun_Cp_copy_file(const char *src_file, const char *dest_file) //标准文件复制
{
    int src_file_descriptor = open(src_file, O_RDONLY);
    int dest_file_descriptor = creat(dest_file, O_WRONLY);
    unsigned char BUFFER[500];
    while (read(src_file_descriptor, BUFFER, sizeof(BUFFER)) > 0) //将源文件内容先存入缓冲区并写入目标文件中
        write(dest_file_descriptor, BUFFER, sizeof(BUFFER));
    Fun_Cp_Change_A(src_file, dest_file);
    close(src_file_descriptor);
    close(dest_file_descriptor);
}

void Fun_Cp_copy_dir(const char *src_dir, const char *dest_dir) //建立文件目录文件
{
    mkdir(dest_dir, 0777);              //以可读可写可执行建立新文件目录
    Fun_Cp_Change_A(src_dir, dest_dir); //更改新文件目录属性
}

void Fun_Cp_copy_softlink(const char *src_file, const char *dest_file) //复制软链接文件
{
    char BUFFER[500];
    int length = readlink(src_file, BUFFER, sizeof(BUFFER));
    if (length > 0)
    {
        if (symlink(BUFFER, dest_file) == -1)
            perror("symlink");
    }
    Fun_Cp_Change_A(src_file, dest_file);
}

void Fun_Cp_dfs(char *src_path, char *dest_path) //深度优先搜索遍历文件夹中内容
{
    DIR *src_dir = opendir(src_path);
    DIR *dest_dir = opendir(dest_path);
    struct dirent *entry = NULL;
    struct stat state_tar;
    while ((entry = readdir(src_dir)) != NULL)
    {
        lstat(entry->d_name, &state_tar);
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
            continue;
        if (S_ISLNK(state_tar.st_mode)) //当是软连接文件时
        {
            char src_file[500];
            char dest_file[500];
            strcpy(src_file, src_path);
            Fun_Cp_Change_P(src_file, entry->d_name);
            strcpy(dest_file, dest_path);
            Fun_Cp_Change_P(dest_file, entry->d_name);
            Fun_Cp_copy_softlink(src_file, dest_file);
        }
        else if (S_ISREG(state_tar.st_mode)) //当是标准文件时
        {
            char src_file[500];
            char dest_file[500];
            strcpy(src_file, src_path);
            Fun_Cp_Change_P(src_file, entry->d_name);
            strcpy(dest_file, dest_path);
            Fun_Cp_Change_P(dest_file, entry->d_name);
            Fun_Cp_copy_file(src_file, dest_file);
        }
        else if (S_ISDIR(state_tar.st_mode)) //当是目录时
        {
            char src[500];
            char dest[500];
            strcpy(src, src_path);
            Fun_Cp_Change_P(src, entry->d_name);
            strcpy(dest, dest_path);
            Fun_Cp_Change_P(dest, entry->d_name);
            Fun_Cp_copy_dir(src, dest);
            Fun_Cp_dfs(src, dest);
        }
    }
}

void Fun_Cp(string Folder_Src, string Folder_Des)
{
    if (Folder_Des.find('.') != Folder_Des.npos)
    {
        Fun_Cp_copy_file(Folder_Src.c_str(), Folder_Des.c_str());
        return;
    }
    else
    {
        if (!(access(Folder_Des.c_str(), F_OK) == 0)) //如果没有目标文件夹则建立目标文件夹
        {
            mkdir(Folder_Des.c_str(), 0777);
        }
    }

    char src[100], dest[100];
    char *cur_dir = getcwd(NULL, 0);
    struct stat state_tar;
    lstat(Folder_Src.c_str(), &state_tar);
    if (S_ISDIR(state_tar.st_mode)) //当时文件夹时
    {
        if (chdir(Folder_Src.c_str()))
        {
            perror("chdir");
            return;
        }
        strcpy(src, getcwd(NULL, 0));
        chdir(cur_dir);
        lstat(Folder_Des.c_str(), &state_tar);
        if (S_ISDIR(state_tar.st_mode))
        {
            if (chdir(Folder_Des.c_str()))
            {
                perror("chdir");
                return;
            }
            strcpy(dest, getcwd(NULL, 0));
            chdir(cur_dir);
            chdir(dest);
            chdir(src);
            Fun_Cp_dfs(src, dest);
        }
    }

    else //当时文件时
    {
        char dest[100];
        lstat(Folder_Des.c_str(), &state_tar);
        if (S_ISDIR(state_tar.st_mode))
            strcpy(dest, getcwd(NULL, 0));
        else
        {
            strcpy(dest, "./");
            strcat(dest, Folder_Des.c_str());
        }
        Fun_Cp_copy_file(Folder_Src.c_str(), Folder_Des.c_str());
    }
    Fun_Cd("..");
    return;
}
